package com.videri.match.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.HistoryListener;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.KeyboardListenerAdapter;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.TextBoxBase;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.VerticalSplitPanel;
import com.google.gwt.user.client.ui.Widget;
import com.videri.match.client.MatchSink.MatchSinkInfo;

/**
 * VideriMatchSink is the primary class for the videria GUI. 
 * This implements EntryPoint, making it the index class for the 
 * site.  Modeled after the example project, KitchenSink, it 
 * makes a frame using widgets with a central panel in which 
 * the sinks are placed.  
 * @author Mike Bauer, Justin Mullen
 * @version 13 November 2007
 */
public class VideriMatchSink implements EntryPoint, HistoryListener {

	private RootPanel rootpanel = RootPanel.get();
	private MatchSinkList list = new MatchSinkList();
	private MatchSinkInfo currentMatchSinkInfo;
	private MatchSink currentMatchSink;
	private VerticalPanel panel = new VerticalPanel();
	private VerticalPanel sidebar = new VerticalPanel();
	private HorizontalPanel container = new HorizontalPanel();
	private boolean loggedIn = false;
	public static String cookie = null;
	private static final String ROOT_STYLE = "match-root-style";
	
	/**
	 * Creates (via. separate methods) and puts together all 
	 * of the different panels into one main panel.  
	 * Essentially the "main" method of the class.  
	 * History token allows the tracking of the user so that the
	 * browser's back and next buttons are functional, which 
	 * would not be possible with a TabbedPanel.
	 */
	public void onModuleLoad() {
		loadMainPage();
	}

	/**
	 * Loads the main page with the toolbar for making/playing games, entering SRM's, etc..
	 */
	public void loadMainPage()
	{
		// add style to root panel
		rootpanel.addStyleName(ROOT_STYLE);
		// Load all the sinks.
	    loadSinks();

	    // Assemble the tabs and sinks
	    panel.add(list);
	    panel.setWidth("100%");

	    // Assemble the side bar
	    sidebar.add(loadIcon());
	    sidebar.add(makeHTMLSidebar());
	    
	    // Put everything in the container
	    container.add(sidebar);
	    container.add(panel);
	    container.setCellWidth(sidebar, "160px");
	    
	    
	    History.addHistoryListener(this);
	    rootpanel.add(container);
	    
	    // Show the initial screen.
	    String initToken = History.getToken();
	    if (initToken.length() > 0) {
	      onHistoryChanged(initToken);
	    } else {
	      showInfo();
	    }
	}
	
	/**
	 * Returns a widget, in this case the Duke D icon, from the web.  
	 * For future releases the icon should be included with the source
	 * or personally hosted online.
	 * @return The logo for the page, icon 
	 */
	private Widget loadIcon() {
		Image icon = new Image("http://immaterialsn.files.wordpress.com/2007/05/duke-logo.thumbnail.jpg");
		return icon;
	}

	/**
	 * Imports HTML for a static HTMLPanel that will be part of the main page's 
	 * frame.  Allowing this class to return a widget leaves the option to exchange
	 * a dynamic panel in future versions.
	 * @return The HTMLPanel info
	 */
	private Widget makeHTMLSidebar() {
		VerticalPanel sidebar = new VerticalPanel();
		HTMLPanel info = new HTMLPanel("<h3>Top Users</h3>"+
				"<ol><li>topGam3r</li>"+
				"<li>JohnDoe</li>"+
				"<li>ola</li></ol>");
		sidebar.add(info);
		sidebar.add(loginPanel());
		return sidebar;
	}

	private Widget loginPanel() {
		if(loggedIn == true){
			return new HTMLPanel("Welcome!");
		}
		final VerticalPanel vp = new VerticalPanel();
		
		FlexTable loginTable = new FlexTable();
		vp.add(loginTable);
		
		Label handleLabel = new Label("Username:");
		loginTable.setWidget(0, 0, handleLabel);
		
		Label passLabel = new Label("Password:");
		loginTable.setWidget(1, 0, passLabel);
		
		final TextBox handleText = new TextBox();
		loginTable.setWidget(0, 1, handleText);
		handleText.setWidth("50px");
		
		final PasswordTextBox pswdText =  new PasswordTextBox();
		loginTable.setWidget(1, 1, pswdText);
		pswdText.setWidth("50px");
		
		Button signInButton = new Button();
		loginTable.setWidget(2, 1, signInButton);
		signInButton.addClickListener(new ClickListener() {
			public void onClick(Widget sender) {
				final String handle = handleText.getText();
				String passwd = pswdText.getText();
				if (handle.length() == 0 || 
						passwd.length() == 0){
					Window.alert("Username or password is not entered.");
				}
				else {
					//log user in
					
					//This is the awesome code right here!!!
					VideriMatchServiceAsync videriServices = (VideriMatchServiceAsync)GWT.create(VideriMatchService.class);
					
					  ServiceDefTarget endpoint = (ServiceDefTarget) videriServices;
					  String moduleRelativeURL = GWT.getModuleBaseURL()+"matching";
					  endpoint.setServiceEntryPoint(moduleRelativeURL);
					
					AsyncCallback callback = new AsyncCallback() {                
						public void onSuccess(Object result) {
							//user logged in
							//store cookie
							cookie = (String) result;
							if(cookie.equals("")){ //blank cookie == failed login
								Window.alert("Failed Login");
							}
							else{ //success!
								//refresh panel
								loggedIn = true;
								vp.clear();
								vp.add(new HTMLPanel("Welcome "+handle+"!"));
								
								//logout button
								Button signOutButton = new Button();
								signOutButton.addClickListener(new ClickListener() {
									public void onClick(Widget sender) {
										
										//This is the awesome code right here!!!
										VideriMatchServiceAsync videriServices = (VideriMatchServiceAsync)GWT.create(VideriMatchService.class);

										ServiceDefTarget endpoint = (ServiceDefTarget) videriServices;
										String moduleRelativeURL = GWT.getModuleBaseURL()+"matching";
										endpoint.setServiceEntryPoint(moduleRelativeURL);

										AsyncCallback callback = new AsyncCallback() {                
											public void onSuccess(Object result) {
												//user logged out.
												Boolean temp = (Boolean)result;
												boolean out = temp.booleanValue();
												if(out){
													loggedIn = false;
													//recreate login pane
												}
												else{
													Window.alert("Failed Logout.");
												}
												
											}                
											public void onFailure(Throwable caught) {                    
												Window.alert("Problem connecting to server during logout.");              
											}            
										};
										videriServices.logoutUser(cookie, callback);
									}
								});
							}	
						}                
						public void onFailure(Throwable caught) {                    
							Window.alert("Problem connecting to server during login.");              
						}            
					};
					videriServices.loginUser(handle, passwd, callback);
				}
			}
		});	
		//TODO: add keyboard listener so hitting return works the same as pressing login.
		signInButton.setText("log in");
		return vp;
	}

	/**
	 * References and keeps track of the history token so that back and next buttons
	 * are functional within the browser, and the most recently viewed tab loads by
	 * default.
	 */
	public void onHistoryChanged(String historyToken) {
		MatchSinkInfo matchInfo = (MatchSinkInfo)list.find(historyToken);
		if(matchInfo==null){
			showInfo();
			return;
		}
		show(matchInfo, false);
	}
	
	/**
	 * Updates the frame to display the chosen sink.
	 * @param info The desired sink's info field
	 * @param affectHistory whether or not this should affect the history.
	 */
	public void show(MatchSinkInfo info, boolean affectHistory) {
	    // Don't bother re-displaying the existing sink. This can be an issue
	    // in practice, because when the history context is set, our
	    // onHistoryChanged() handler will attempt to show the currently-visible
	    // sink.
	    if (info == currentMatchSinkInfo) {
	      return;
	    }
	    
	    if(info.getName().equals("Play Matching Game") && !affectHistory)
	    	info = list.find("Choose a Matching");
//	    else
//	    	affectHistory = false;
	    
	    currentMatchSinkInfo = info;

	    // Remove the old sink from the display area.
	    if (currentMatchSink != null) {
	      currentMatchSink.onHide();
	      panel.remove(currentMatchSink);
	    }

	    // Get the new sink instance, and display its description in the
	    // sink list.
	    currentMatchSink = info.getInstance();
	    list.setSinkSelection(info.getName());

	    // If affectHistory is set, create a new item on the history stack. This
	    // will ultimately result in onHistoryChanged() being called. It will call
	    // show() again, but nothing will happen because it will request the exact
	    // same sink we're already showing.
	    if (affectHistory) {
	      History.newItem(info.getName());
	    }
	    
	    if(info.getName().equals("Choose a Matching")){
	    	VideriChooser temp = (VideriChooser)currentMatchSink;
	    	temp.setSink(this);
	    }
	    
	    // Display the new sink.
	    currentMatchSink.setVisible(false);
	    panel.add(currentMatchSink);
	    panel.setCellHorizontalAlignment(currentMatchSink, VerticalPanel.ALIGN_CENTER);	    
	    currentMatchSink.setVisible(true);	    
	    currentMatchSink.onShow();
	  }
	
	/**
	 * Once a game is chosen, the tabs should switch to the game tab.
	 * This initializes the instance of the game.
	 * @param m matching game chosen
	 */
	public void doneChoosing(Matching m){
		MatchSinkInfo info=list.find("Play Matching Game");
		if(info==null){
			showInfo();
			return;
		}
    	PlayGame inst = (PlayGame)info.getInstance();
    	inst.initialize(m);
    	show(info,true);
	}

	  /**
	   * Adds all sinks to the list. Note that this does not create actual instances
	   * of all sinks yet (they are created on-demand). This can make a significant
	   * difference in startup time.
	   */
	  protected void loadSinks() {
		list.addSink(VideriHome.init());
		list.addSink(CreateAccount.init());
	    list.addSink(CreateGame.init());
	    list.addSink(VideriChooser.init());
	    list.addSink(PlayGame.init());
	    list.addSink(Head2Head.init());
	    list.addSink(SRMRoom.init());
	  }

	  // This method will only be called on startup, it should display the first sink
	  private void showInfo() {
	    show(list.find("Videri Home"), false);
	  }
}
